﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CriptoHash.Criptografia;

namespace CriptoHash.Criptografia
{
    public class Criptografador
    {
        private ushort[] table = new ushort[256];
        private const ushort polynomial = 0xA001;

        public Criptografador()
        {
            #region Declarações e Inicializações das variáveis do CRC 16
            ushort value;
            ushort temp;
            for (ushort i = 0; i < table.Length; ++i)
            {
                value = 0;
                temp = i;
                for (byte j = 0; j < 8; ++j)
                {
                    if (((value ^ temp) & 0x0001) != 0)
                    {
                        value = (ushort)((value >> 1) ^ polynomial);
                    }
                    else
                    {
                        value >>= 1;
                    }
                    temp >>= 1;
                }
                table[i] = value;
            }
            #endregion
        }

        /// <summary>
        /// Método para trazer a lista de todos os métodos de hash disponíveis na aplicação
        /// </summary>
        /// <returns></returns>
        public static List<string> ListaMetodos()
        {
            List<string> lista = new List<string>();

            lista.Add("C# Comum");
            lista.Add("Congruente S1");
            lista.Add("Collatz");
            lista.Add("Soma de Senos");
            lista.Add("CRC-16");
            lista.Add("Treze");

            return lista;
        }

        public string ChamarMetodoHash(string nomeMetodo, int valor)
        {
            switch (nomeMetodo)
            {
                case "C# Comum":
                    {
                        int hashString = valor.GetHashCode();
                        return hashString.ToString();
                    }
                case "Congruente S1":
                    {
                        RandomGenerator random = new RandomGenerator(valor, 400);
                        return random.NumeroAleatorio(RandomGenerator.MetodoRandom.LINEAR_CONGRUENTE).ToString();
                    }
                case "Collatz":
                    {
                        return Collatz(valor).ToString();
                    }
                case "Soma de Senos":
                    {
                        return SomaSenos(valor);
                    }
                case "CRC-16":
                    {
                        return CRC16(valor);
                    }
                case "Treze":
                    {
                        return (valor * 13).ToString();
                    }
            }
            return "";
        }

        /// <summary>
        /// Método para chamar o método de hash passado como parâmetro para o texto passado
        /// </summary>
        /// <param name="nomeMetodo"></param>
        /// <param name="texto"></param>
        /// <returns></returns>
        public string ChamarMetodoHash(string nomeMetodo, string texto)
        {
            int valorTexto = TransformarTextoToInt(texto);

            return ChamarMetodoHash(nomeMetodo, valorTexto);
        }

        #region Métodos de Calcular Hash Personalizados

        private int TransformarTextoToInt(string texto)
        {
            int valor = 0;

            for (int i = 0; i < texto.Length; i++)
            {
                int modificador = 1;
                if (i % 2 == 0)
                {
                    modificador += 1 + (int)i%Matematica.MenorPrimo(13);
                }
                string caractere = texto[i].ToString();
                byte[] asciiCode = Encoding.ASCII.GetBytes(caractere);

                int valorInteiro = 0;

                for (int j = 0; j < asciiCode.Length; j++)
                {
                    valorInteiro += (int)asciiCode[j];
                }

                valor += modificador * valorInteiro;
            }

            return valor;
        }

        private int Collatz(int valor)
        {
            if (valor <= 1)
            {
                return 1;
            }
            else
            {
                if (valor % 2 == 0)
                {
                    return valor + Collatz(valor / 2);
                }
                else
                {
                    return valor + Collatz(valor * 3 + 1);
                }
            }
        }

        private string SomaSenos(int valor)
        {
            int valorAux = valor << 0x02;

            byte[] intBytes = BitConverter.GetBytes(valorAux);
            int resultado = 0;
            double quociente = 0;

            for (int i = 0; i < intBytes.Length; i++)
            {
                if (intBytes[i] == 0)
                {
                    quociente++;
                }
                else
                {
                    double ajustado = intBytes[i] << 0x02;
                    quociente += (1000*Math.Sin(ajustado));
                }
            }

            resultado = (int)quociente;

            return resultado.ToString();
        }

        private string CRC16(int valor)
        {
            byte[] intBytes = BitConverter.GetBytes(valor);
            ushort resultado = ComputeChecksum(intBytes);

            return resultado.ToString();
        }

        private ushort ComputeChecksum(byte[] bytes)
        {
            ushort crc = 0;
            for (int i = 0; i < bytes.Length; ++i)
            {
                byte index = (byte)(crc ^ bytes[i]);
                crc = (ushort)((crc >> 8) ^ table[index]);
            }
            return crc;
        }

        #endregion
    }
}
